/********************************************************************************/
/* 	     memberfuncts.c: module that defines member set functions:		*/
/*		    setOpcode, setRegisters, setReg, setFloat			*/
/********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "DecodedStructs.h"

DECODED *CreateDeInst()
{
        DECODED *DeInst=NULL;
        DeInst=(DECODED *)malloc(sizeof(DECODED));
        if (! DeInst)
        {
                printf("Out of memory! Aborting...\n");
                exit(10);
        }

        /*Allocating enough space for a 10 character string in each piece of the struct*/
        DeInst->opcode=(char *)malloc(sizeof(char)*STR_SIZE);

        if (! DeInst->opcode)
        {
                printf("Out of memory! Aborting...\n");
                exit(10);
        }

        DeInst->reg1=(char *)malloc(sizeof(char)*STR_SIZE);

        if (! DeInst->reg1)
        {
                printf("Out of memory! Aborting...\n");
                exit(10);
        }

        DeInst->reg2=(char *)malloc(sizeof(char)*STR_SIZE);

        if (! DeInst->reg2)
        {
                printf("Out of memory! Aborting...\n");
                exit(10);
        }

        DeInst->reg3=(char *)malloc(sizeof(char)*STR_SIZE);

        if (! DeInst->reg3)
        {
                printf("Out of memory! Aborting...\n");
                exit(10);
        }

        return DeInst;
}

void DeleteDeInst(DECODED *DeInst)
{
	assert(DeInst);
	if(!(DeInst))
	{
		if(!(DeInst->opcode))
		{
			assert(DeInst->opcode);
			free(DeInst->opcode);
			DeInst->opcode = NULL;
		}
	
		if(!(DeInst->reg1))
		{
			assert(DeInst->reg1);
			free(DeInst->reg1);
			DeInst->reg1 = NULL;
		}
	
		if(!(DeInst->reg2))
		{
			assert(DeInst->reg2);
			free(DeInst->reg2);
			DeInst->reg2 = NULL;
		}
	
		if(!(DeInst->reg3))
		{
			assert(DeInst->reg3);
			free(DeInst->reg3);
			DeInst->reg3 = NULL;
		}
	
		free(DeInst);
		DeInst=NULL;
		return;
	}
	else
	{
		perror("NOTHING TO FREE...aborting!");
		exit(1);
	}
}

/* setOpcode */

void setOpcode(INSTU u, DECODED *d)
{
	char *temp_str = malloc(sizeof(char)*20);

        const char *functA[32] = 
	{ 
		NULL, "tlbr", "tlbwi", NULL, NULL, NULL, "tlbwr", NULL,
                "tlbp", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
		"eret", NULL, NULL, NULL, NULL, NULL, NULL, "deret" 
	};

        const char *functCsub[2] = { "movf.", "movt." };

        const char *functC[64] = 
	{
		"add", "sub", "mul", "div",
		"sqrt", "abs", "mov", "neg",
		NULL, NULL, NULL, NULL,
		"round.w", "trunc.w", "cell.w", "floor.w",
		NULL, functCsub[u.B17.a], "movz", "movn",
		NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL,
		"cvt.s", "cvt.d", NULL, NULL,
		"cvt.w", NULL, NULL, NULL,
		NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL,
		"c.f", "c.un", "c.eq", "c.ueq",
		"c.olt", "c.ult", "c.ole", "c.ule", 
		"c.sf", "c.ngle", "c.seq", "c.ngl",
                "c.lt", "c.nge", "c.le", "c.ngt" 
	};

        const char *rsSubArray[4] = { "f","t","fl","tl" };

	const char *z1Array[3] = 
	{ 
		NULL,
		strcat(strcat(strcpy(temp_str, "bc"), "1"), rsSubArray[u.I.tal]),
		strcat(strcat(strcpy(temp_str, "bc"), "2"), rsSubArray[u.I.tal]) 
	}; 

	const char *z2Array[3] = 
	{ 
		functA[u.R.funct],
		strcat(strcpy(temp_str, functC[u.R.funct]), ".s"),
		strcat(strcpy(temp_str, functC[u.R.funct]), ".s") 
	};

	const char *z3Array[3] = 
	{ 
		strcat(strcpy(temp_str, functC[u.R.funct]), ".d"),
		strcat(strcpy(temp_str, functC[u.R.funct]), ".d"),
		strcat(strcpy(temp_str, functC[u.R.funct]), ".d") 
	};

        const char *rsArray[32] = 
	{ 
		"mfc", NULL, "cfc", NULL,
		"mtc", NULL, "ctc", z1Array[u.R.opcode-16],
		NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL,
                z2Array[u.R.opcode-16], z3Array[u.R.opcode-16], NULL, NULL,
		NULL, NULL, NULL, NULL, 
		NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL 
	};

        const char *rtArray[32] = 
	{ 
		"bltz", "bgez", "bltzl", "bgezl",
		NULL, NULL, NULL, NULL,
                "tgei", "tgeiu", "tlti", "tltiu",
		"tegi", NULL, "tnei", NULL,
                "bltzal", "bgezal", "bltzall", "bgczall",
		NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL 
	};

        const char *functBsub[2] = { "movf", "movt" };

        const char *functB[64] = 
	{ 
		"sll", functBsub[u.B17.a], "srl", "sra",
		"sllv", NULL, "srlv", "srav",
		"jr", "jalr", "movz", "movn",
		"syscall", "break", NULL, "sync",
		"mfhi", "mthi", "mflo", "mtlo",
		NULL, NULL, NULL, NULL,
		"mult", "multu", "div", "divu",
		NULL, NULL, NULL, NULL,
		"add", "addu", "sub", "subu",
		"and", "or", "xor", "nor",
		NULL, NULL, "slt", "sltu",
		NULL, NULL, NULL, NULL,
                "tge", "tgeu", "tlt","tltu",
		"teq", NULL, "tne", NULL,
		NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL 
	};

        const char *functD[64] = 
	{  
		"madd", "maddu", "mul", NULL,
		"msub", "msubu", NULL, NULL,
                NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL,
                NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL,
                NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL,
                "clz", "clo", NULL, NULL,
		NULL, NULL, NULL, NULL,
                NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL,
                NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL,
                NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL 
	};

	const char *zOpArray[3] = 
	{ 
	((u.R.rs < 16)?strcat(strcpy(temp_str, rsArray[u.R.rs]), "0"):rsArray[u.R.rs]),
	((u.R.rs < 16)?strcat(strcpy(temp_str, rsArray[u.R.rs]), "1"):rsArray[u.R.rs]),
	((u.R.rs < 16)?strcat(strcpy(temp_str, rsArray[u.R.rs]), "2"):rsArray[u.R.rs])
	};

        const char *OpCodeArray[64] = 
	{ 
		functB[u.R.funct], rtArray[u.R.rt], "j", "jal",
                "beq", "bne", "blez", "bgtz",
		"addi", "addiu", "slti", "sltiu",
		"andi", "ori", "xori", "lui",
                zOpArray[0], zOpArray[1], zOpArray[2], NULL, 
                "beql", "bnel", "blezl", "bgtzl",
                functD[u.R.funct],
                functD[u.R.funct], 
                functD[u.R.funct],
                functD[u.R.funct], 
                functD[u.R.funct], 
                functD[u.R.funct],
                functD[u.R.funct], 
                functD[u.R.funct], 
                "lb", "lh", "lwl", "lw", 
		"lbu", "lhu", "lwr", NULL, 
                "sb", "sh", "swl", "sw", 
		NULL, NULL, "swr", "cache", 
                "ll" "lwc1", "lwc2", "pref", 
		NULL, "ldc1", "ldc2", NULL,
                "sc", "swc1", "swc2", NULL, 
		NULL, "sdc1", "sdc2", NULL 
	};

	/* Instruction must be valid */
        if(OpCodeArray[u.R.opcode] != NULL)
	{
		/* If the opcode array is valid then copy the memory of the string to the*/
		/* memory allocated for the opcode string in the data structure 	 */
		memcpy(d->opcode, OpCodeArray[u.R.opcode], STR_SIZE);
		return;
	}
	else
	{
		/* The instruction is invalid. Print a message a throw an error 	*/
		perror("Invalid Instruction!\n");
		d->opcode = NULL;
		return;
	}
}

/* setRegisters */

void setRegisters(INSTU u, DECODED *d)
{

	/* RTYPE CASING */
	if(u.D.opcode==0)
	{
		/*rd, rt, fd*/
		if(u.R.funct==0 || u.R.funct==2 || u.R.funct==3) 
		{
			char *str=NULL;
			str=(char *)malloc(sizeof(char)*10);
			if (! str)
			{
                		printf("Out of memory! Aborting...\n");
                		exit(10);
			}

			else
			{
				setReg(u.R.rd, d->reg1);
				setReg(u.R.rt, d->reg2);
				sprintf(str, "%d", u.R.fd);
				d->reg3 = str;
				return;
			}
		}

		/*rd, rt, rs*/
		else if(u.R.funct==4 || u.R.funct==6 || u.R.funct==7)
		{
			setReg(u.R.rd, d->reg1);
			setReg(u.R.rt, d->reg2);
			setReg(u.R.rs, d->reg3);
			return;
		}

		/*rd, rs*/
		else if(u.R.funct==9)
		{
			setReg(u.R.rd, d->reg1);
			setReg(u.R.rs, d->reg2);
			d->reg3 = '\0';
			return;
		}

		/*syscall, break, sync*/
		else if(u.R.funct==12 || u.R.funct==13 || u.R.funct==15)
		{
			d->reg1 = '\0';
			d->reg2 = '\0';
			d->reg3 = '\0';
			return;
			/*Not sure about sync. Can't find the format*/
		}

		/*rd, rs, rt */
		else if( (u.R.funct >=32 && u.R.funct <= 47) || u.R.funct==10 || u.R.funct==11)
		{
			setReg(u.R.rd, d->reg1);
			setReg(u.R.rs, d->reg2);
			setReg(u.R.rt, d->reg3);
			return;
		}

		/*rd*/
		else if(u.R.funct==16 || u.R.funct==18)
		{
			setReg(u.R.rd, d->reg1);
			d->reg2 = '\0';
			d->reg3 = '\0';
			return;
		}

		/*rs*/
		else if(u.R.funct==8 || u.R.funct==17 || u.R.funct==19)
		{
			setReg(u.R.rs, d->reg1);
			d->reg2 = '\0';
			d->reg3 = '\0';
			return;
		}

		/*rs, rt*/
		else if(u.R.funct==24 || u.R.funct==25 || (u.R.funct>=48 && u.R.funct<=54) )
		{
			setReg(u.R.rs, d->reg1);
			setReg(u.R.rt, d->reg2);
			d->reg3 = '\0';
			return;
		}

	}/*End of RTYPE if*/

	/*JTYPE CASES*/
	else if(u.D.opcode==2 || u.D.opcode==3)
	{
		char *target = NULL;
		target=(char *)malloc((sizeof(char)*4));
		if (! target)
        	{
                	printf("Out of memory! Aborting...\n");
                	exit(10);
        	}

		else
		{
			sprintf(target, "%d", u.J.target);
			d->reg1 = target;
			d->reg2 = '\0';
			d->reg3 = '\0';
			return;
		}
	}/*END OF JTYPE CASES*/

	/*FLOATING POINT REGISTERS*/
	
	else if(u.D.opcode == 16 || u.D.opcode == 17 || u.D.opcode == 18 || u.D.opcode == 19)
	{
        	if( (u.R.funct == 5) || (u.R.funct == 14) || (u.R.funct == 33) || (u.R.funct == 32) ||
			(u.R.funct == 36) || (u.R.funct == 15) || (u.R.funct == 6) || (u.R.funct == 7) || 
			(u.R.funct == 12) || (u.R.funct == 4) || (u.R.funct == 13) )
		{
			setFloat(u.R.fd, d -> reg1); 
			setFloat(u.R.rd, d -> reg2);
			d -> reg3 = '\0';
			return;
	        }

		else if(u.R.funct == 0 || u.R.funct == 3 || u.R.funct == 2 || u.R.funct 
		== 1)
		{
			setFloat(u.R.fd, d -> reg1);
			setFloat(u.R.rd, d -> reg2);
			setFloat(u.R.rt, d -> reg3);
			return;
		}
		else if(u.R.funct == 19 || u.R.funct == 18)
		{
			setFloat(u.R.fd, d -> reg1);
			setFloat(u.R.rd, d -> reg2);
			setReg(u.R.rt, d -> reg3);
			return;
		}
		else if(u.R.funct == 24)
		{
			d -> reg1 = '\0';
			d -> reg2 = '\0';
			d -> reg3 = '\0';
			return;
		}
	}

	/*ITYPE CASING*/
	/*rt, rs, imm*/
	else if(u.D.opcode>=8 && u.D.opcode<=14)
	{
		char *str=NULL;
		str=(char *)malloc(sizeof(char)*10);
		if (! str)
		{
			printf("Out of memory! Aborting...\n");
            exit(10);
		}

		else
		{
			setReg(u.R.rt, d->reg1);
			setReg(u.R.rs, d->reg2);
			sprintf(str, "%d", u.I.offset);
			d->reg3 = str;
			return;
		}
	}

	/*rt, imm*/
	else if( (u.D.opcode>=32 && u.D.opcode<=38) || u.D.opcode==15
				|| (u.D.opcode>=40 && u.D.opcode<43) || u.D.opcode==46)
	{
		char *str=NULL;
		str=(char *)malloc(sizeof(char)*10);
		if (! str)
		{
			printf("Out of memory! Aborting...\n");
            exit(10);
		}

		else
		{
			setReg(u.R.rt, d->reg1);
			sprintf(str, "%d", u.I.offset);
			d->reg2 = str;
			d->reg3 = '\0';
			return;
		}
	}

	/*ft, address*/
	else if(u.D.opcode==49 || u.D.opcode==56 || u.D.opcode==57 || u.D.opcode==61)
	{
		char *str=NULL;
		str=(char *)malloc(sizeof(char)*10);
		if (! str)
		{
			printf("Out of memory! Aborting...\n");
            exit(10);
		}

		else
		{
			setReg(u.R.rt, d->reg1);
			sprintf(str, "%d", u.I.offset);
			d->reg2 = str;
			d->reg3 = '\0';
			return;
		}
	}
	/* None of the cases! */
	else
	{
		/* The instruction is invalid. Print a message a throw an error 	*/
		perror("Invalid Instruction!\n");
		d->opcode = NULL;
		printf("Uh Oh!\n");
		return;
	}
}

/* setReg */
void setReg(int i, char *c)
{
	assert(c);
	const char *Registers[32] = 
	{
		"$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3", 
		"$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
		"$s0", "$s1", "$s3", "$s4", "$s5", "$s6", "$s7", "$t8",
		"$t9", "$k0", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra"
	};	
	memcpy(c, Registers[i], STR_SIZE);
	return;
}

void setFloat(int i, char *c)
{
	assert(c);
	const char *Float[32] =
	{
		"$f0", "$f01", "$f02", "$f03", "$f04", "$f05", "$f06", "$f07", "$f08",
		"$f09", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", 
		"$f18", "$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26",
		"$f27", "$f28", "$f29", "$f30", "$f31"
	};
	memcpy(c, Float[i], STR_SIZE);
}
